package com.lumlord.log;


import com.alibaba.fastjson.JSONObject;
import com.lumlord.util.GsonTools;
import com.lumlord.util.SyncLog;
import lombok.extern.java.Log;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @describe: 实现controller的日志切面
 * @author: gpf
 * @date: 2021-1-29 14:43:40
 */
@Aspect
@Component
@Order(1)
@Log
public class ControllerLogAspect {
    ThreadLocal<Long> startTime = new ThreadLocal<>();
    ThreadLocal<LogAction> webLogThreadLocal = new ThreadLocal<>();

    @Pointcut("execution(* com.lumlord.controller.*.*(..))")
    public void controllerAspect() {
    }

    @Pointcut("execution(* com.lumlord.rpc.*.*(..))")
    public void feignClientPointCut() {
    }


    @Around(value = "feignClientPointCut()")
    public Object doAroundFeignClientPointCut(ProceedingJoinPoint joinPoint) {

        Object obj = null;
        ConcurrentHashMap map = new ConcurrentHashMap();
        map.put("method", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        map.put("args", GsonTools.toJson(joinPoint.getArgs()));

        Class clazz = joinPoint.getSignature().getDeclaringType();
        boolean isNoCheckAuth = clazz.isAnnotationPresent(FeignClient.class);
        if (isNoCheckAuth) {
            //类名前注解
            FeignClient feignClient = (FeignClient) clazz.getAnnotation(FeignClient.class);

            map.put("serverName", feignClient.name());
            map.put("url", feignClient.url());
        }

        try {
            obj = joinPoint.proceed(); //可以加参数
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        //   map.put("ret",GsonTools.toJson(obj));
        webLogThreadLocal.get().setZipkin(webLogThreadLocal.get().getZipkin() + GsonTools.toJson(map));
        return obj;
    }

    @Before("controllerAspect()")
    public void doBefore(JoinPoint joinPoint) {
        // 接收到请求，记录请求内容
        startTime.set(System.currentTimeMillis());
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        LogAction log = new LogAction();


        log.setHttpMethod(request.getMethod());
        log.setClassMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        log.setUrl(request.getRequestURL().toString());
        log.setIp(request.getRemoteAddr());
        log.setArgs(Arrays.toString(joinPoint.getArgs()));
        log.setLogType("HTTP");
        log.setReqJsonParams(GsonTools.toJson(request.getParameterMap()));
        log.setReqParams(request.getQueryString());
        log.setJsonRequest(getJsonRequest(request));
        log.setServerName(SyncLog.staticSpringApplicationName);

        webLogThreadLocal.set(log);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "controllerAspect()", throwing = "e")
    public void exception(JoinPoint joinPoint, Throwable e) {
        LogAction log = webLogThreadLocal.get();
        log.setException(getTrace(e));
        log.setCause(e.toString());
        log.setSpendTime(System.currentTimeMillis() - startTime.get());
        saveInfluxdb(log);
    }


    @After("controllerAspect()")
    public void after(JoinPoint joinPoint) {
        LogAction log = webLogThreadLocal.get();
        log.setException("success");
        log.setCause(null);
        log.setSpendTime(System.currentTimeMillis() - startTime.get());
        saveInfluxdb(log);
    }

    public String getTrace(Throwable t) {
        StringWriter stringWriter = new StringWriter();
        PrintWriter writer = new PrintWriter(stringWriter);
        t.printStackTrace(writer);
        StringBuffer buffer = stringWriter.getBuffer();
        return buffer.toString();
    }


    private void saveInfluxdb(LogAction log) {

        Map tags = new HashMap<>();
        tags.put("action", log.getUrl());
        Map fields = new HashMap<>();
        fields.put("serverName", log.getServerName());
        fields.put("cause", log.getCause());
        fields.put("classMethod", log.getClassMethod());
        fields.put("exception", log.getException());
        fields.put("logType", log.getLogType());
        fields.put("spendTime", log.getSpendTime());
        fields.put("respParams", log.getReqParams());
        fields.put("args", log.getArgs());
        fields.put("httpMethod", log.getHttpMethod());
        fields.put("ip", log.getIp());
        fields.put("zipkin", log.getZipkin());
        fields.put("jsonRequest", log.getJsonRequest());
        fields.put("reqJsonParams", log.getReqJsonParams());
        //  InfluxDBConfiguration.influxDBConnection.insert("logAction",tags,fields,0, TimeUnit.NANOSECONDS);
        SyncLog.log(tags, fields, log.getClassMethod(),true);
    }


    private String getJsonRequest(HttpServletRequest request) {
        String param = null;
        try {
            BufferedReader streamReader = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
            StringBuilder responseStrBuilder = new StringBuilder();
            String inputStr;
            while ((inputStr = streamReader.readLine()) != null)
                responseStrBuilder.append(inputStr);
            JSONObject jsonObject = JSONObject.parseObject(responseStrBuilder.toString());
            param = jsonObject.toJSONString();
        } catch (Exception e) {

        }
        return param;
    }


}